
#include <string.h>		// For memmove() and strlen()
#include <mex.h>
#include "gmatlab.h"

static const gchar* subsFields[] = { "type", "subs" };

/**
 * g_matlab_mxArray_get_pointer:
 * @mxValue: an #mxArray of type %MX_GPOINTER
 *
 * Returns: (transfer none): the single pointer of @mxValue
 */
gpointer g_matlab_mxArray_get_pointer(mxArray *mxValue)
{
  g_return_val_if_fail(mxGetClassID(mxValue) == MX_GPOINTER, NULL);
  TRACE("g_matlab_mxArray_get_pointer returning %ld", ((gpointer*)mxGetData(mxValue))[0]);
  return ((gpointer*)mxGetData(mxValue))[0];
}

/**
 * g_matlab_mxArray_get_utf8:
 * @mxValue: an #mxArray of type %mxCHAR_CLASS
 *
 * Returns: (transfer full): a new %NULL-terminated UTF-8 copy of the string
 */
gchar * g_matlab_mxArray_get_utf8(mxArray *mxValue)
{
  GError *err = NULL;
  gchar *str;
  glong itemsRead = 0;
  g_return_val_if_fail(mxGetClassID(mxValue) == mxCHAR_CLASS, NULL);
  str = g_utf16_to_utf8(mxGetChars(mxValue), mxGetNumberOfElements(mxValue),
                        &itemsRead, NULL, &err);
  if (str == NULL)
    g_matlab_error_with_error("gmatlab:str", err, "Error converting string");
  return str;
}

/**
 * g_matlab_mxArray_from_utf8:
 * @str: (transfer none): a %NULL-terminated UTF-8 encoded string
 *
 * Returns: a #mxArray copy of the string
 */
mxArray * g_matlab_mxArray_from_utf8(const gchar *str)
{
  GError *err = NULL;
  mxArray *mxValue;
  mxChar *data;
  gunichar2 *str16;
  mwSize dims[2] = { 1, 0 };
  glong itemsRead = 0, len, i;

  if (str == NULL) return mxCreateString("");
  str16 = g_utf8_to_utf16(str, -1, &itemsRead, &len, &err);
  if (str16 == NULL) g_matlab_error_with_error("gmatlab:str", err, "Error converting string");
  g_matlab_cleanup_add(g_free, str16);

  dims[1] = len;
  mxValue = mxCreateCharArray(2, dims);
  data = mxGetData(mxValue);
  for (i=0; i < len; i++)
    data[i] = str16[i];
  
  return mxValue;
}

/**
 * g_matlab_sprintf:
 * @format: (transfer none): a printf() style format string
 *
 * Returns: an #mxArray containing the formatted string
 */
mxArray *g_matlab_sprintf(const char *format, ...)
{
  GString *msg = g_string_new(NULL);
  va_list args;
  va_start(args, format);
  g_string_vprintf(msg, format, args);
  va_end(args);
  return g_matlab_mxArray_from_utf8(g_matlab_cleanup_add(g_free,
                                    g_string_free(msg, FALSE)));
}

/**
 * g_matlab_mxArray_common_get_gtype:
 * @mxValue: an #mxArray of an object of a registered type
 *
 * Returns: the #GType of @mxValue
 */
GType g_matlab_mxArray_common_get_gtype(mxArray *mxValue)
{
  mxArray *gtype = mxGetProperty(mxValue, 0, "gtype");
  TRACE("g_matlab_mxArray_common_get_gtype returning %ld", ((GType*)mxGetData(mxValue))[0]);
  return ((GType*)mxGetData(gtype))[0];
}

static gpointer common_get_value(mxArray *mxValue)
{
  mxArray *value = mxGetProperty(mxValue, 0, "value");
  return ((gpointer*)mxGetData(value))[0];
}

static mxArray * common_create(GType gtype, gpointer value, const char *name)
{
  mxArray *ret, *args[2];
  TRACE("common_create(%d, %ld, %s)", gtype, value, name);
  args[0] = mxCreateNumericMatrix(1, 1, MX_GLONG, mxREAL);
  ((glong*)mxGetData(args[0]))[0] = gtype;
  args[1] = mxCreateNumericMatrix(1, 1, MX_GPOINTER, mxREAL);
  ((gpointer*)mxGetData(args[1]))[0] = value;
  mexCallMATLAB(1, &ret, 2, args, name);
  mxDestroyArray(args[0]);
  mxDestroyArray(args[1]);
  return ret;
}

/**
 * g_matlab_mxArray_from_giInfo:
 * @info: a #GIStructInfo or #GIObjectInfo
 *
 * Returns: (transfer none): the glib.GInstance corresponding to @info
 */
mxArray *g_matlab_mxArray_from_giInfo(GIBaseInfo *info)
{
  mxArray *ret, *args[2];
//  g_return_val_if_fail(GI_IS_STRUCT_INFO(info) || GI_IS_OBJECT_INFO(info), NULL);
  // Fetch the repository
  mexCallMATLAB(1, &ret, 0, NULL, "glib.gi.repos");
  // TODO: Replace this with one subsref call
  // TODO: Clean up with mxDestroyArray
  // Fetch the package
  args[0] = ret;	// the repository
  args[1] = mxCreateStructMatrix(1, 1, 2, subsFields);
  mxSetFieldByNumber(args[1], 0, 0, mxCreateString("."));
  mxSetFieldByNumber(args[1], 0, 1,
                     g_matlab_mxArray_from_utf8(g_base_info_get_namespace(info)));
  mexCallMATLAB(1, &ret, 2, args, "subsref");
  // Fetch the class
  args[0] = ret;	// the package
  mxSetFieldByNumber(args[1], 0, 1,
                     g_matlab_mxArray_from_utf8(g_base_info_get_name(info)));
  mexCallMATLAB(1, &ret, 2, args, "subsref");
  /* WARNING: This will re-enter the C side through MATLAB.
   * This is okay as long as the function doesn't escape_init() or
   * cleanup().  */
  return ret;
}

/**
 * g_matlab_mxArray_get_gtype:
 * @mxValue: an #mxArray of type glib.GType or glib.GClass
 *
 * Returns: the #GType of @mxValue
 */
GType g_matlab_mxArray_get_gtype(mxArray *mxValue)
{
  g_return_val_if_fail(g_matlab_check_class(mxValue, "glib.GType"), 0);
  return (GType)common_get_value(mxValue);  
}

/**
 * g_matlab_mxArray_from_gtype:
 * @gtype: a #GType
 *
 * If @gtype is derived from #GObject, returns the glib.GClass corresponding
 * to @gtype.  Otherwise, returns a glib.GType wrapper for @gtype.  If @gtype
 * is a #GObject but cannot be found in the GObject-Introspection repository,
 * a matlab glib.GClass is returned based on the #GITypeInfo of the nearest
 * parent to @gtype in the repository.  But, the returned class will still
 * have #GType @gtype.  New objects created with the returned class will be
 * @gtype objects, as far as glib is concerned, but will only be of the
 * nearest parent for functions accessible by matlab.
 *
 * Returns: a wrapper for @gtype
 */
mxArray * g_matlab_mxArray_from_gtype(GType gtype)
{
  mxArray *ret;
  if (G_TYPE_IS_OBJECT(gtype) || G_TYPE_IS_BOXED(gtype))
  {
    GIBaseInfo *info;
    GType cloak = gtype;
    for (cloak=gtype;
         cloak && !(info = g_irepository_find_by_gtype(NULL, cloak));
         cloak = g_type_parent(cloak)) ;
    if (info)
    {
      if (cloak == gtype)
      {
        g_matlab_cleanup_add((GHookFunc)g_base_info_unref, info);
        return g_matlab_mxArray_from_giInfo(info);
      }
      else
      {
        g_warning("g_matlab_mxArray_from_gtype: Could not find %s in the repository, so treating as glib.%s.%s", g_type_name(gtype), g_base_info_get_namespace(info), g_base_info_get_name(info));
        // Create the glib.GInterface/GClass object
        mxArray *args[4] = { mxCreateNumericMatrix(1, 1, MX_GLONG, mxREAL),      // G_TYPE_GTYPE
          mxCreateNumericMatrix(1, 1, MX_GPOINTER, mxREAL),   // gtype
          mxCreateNumericMatrix(1, 1, MX_GPOINTER, mxREAL),   // GTypeInfo
          g_matlab_sprintf("%s.%s",                           // Full GI name
                           g_base_info_get_namespace(info),
                           g_base_info_get_name(info)) };
        // Matlab object assumes ownership of info
        ((glong*)mxGetData(args[0]))[0] = G_TYPE_GTYPE;
        ((gpointer*)mxGetData(args[1]))[0] = (gpointer)gtype;
        ((gpointer*)mxGetData(args[2]))[0] = info;
        mexCallMATLAB(1, &ret, 4, args, G_TYPE_IS_OBJECT(gtype)
                      ? "glib.GClass" : "glib.GInterface");
        mxDestroyArray(args[0]);
        mxDestroyArray(args[1]);
        mxDestroyArray(args[2]);
        mxDestroyArray(args[3]);
        return ret;
      }
    } else g_warning("g_matlab_mxArray_from_gtype: %s appears not to be available in the introspeciton repository.\n"
                     "Returning a generic GType, which will not be usable for creating objects or calling methods.",
                     g_type_name(gtype));
  }
  
  return common_create(G_TYPE_GTYPE, (gpointer)gtype, "glib.GType");
}

/**
 * g_matlab_mxArray_get_boxed:
 * @mxValue: an #mxArray of type glib.GBoxed
 *
 * Returns: (transfer none): the boxed contents of @mxValue
 */
gpointer g_matlab_mxArray_get_boxed(mxArray *mxValue)
{
  g_return_val_if_fail(g_matlab_check_class(mxValue, "glib.GBoxed"), 0);
  return common_get_value(mxValue);  
}

/**
 * g_matlab_mxArray_from_boxed:
 * @type: the #GType of @data
 * @data: (transfer full): boxed data
 *
 * Returns: an object of type glib.GBoxed encapsulating @data
 */
mxArray * g_matlab_mxArray_from_boxed(GType type, gpointer value)
{
  mxArray *wrapper, *args[3];
  g_return_val_if_fail(value != NULL, NULL);
  if (type)
    g_return_val_if_fail(g_type_is_a(type, G_TYPE_BOXED), NULL);

  TRACE("g_matlab_mxArray_from_boxed Wrap %s: %d, 0x%lx", g_type_is_a(type, G_TYPE_CLOSURE) ? "closure" : "boxed", type, value);
  args[0] = mxCreateNumericMatrix(1, 1, MX_GLONG, mxREAL);
  ((GType*)mxGetData(args[0]))[0] = type;
  args[1] = mxCreateNumericMatrix(1, 1, MX_GPOINTER, mxREAL);
  ((gpointer*)mxGetData(args[1]))[0] = value;
  args[2] = g_matlab_mxArray_from_gtype(type);
  mexCallMATLAB(1, &wrapper, 3, args, g_type_is_a(type, G_TYPE_CLOSURE)
                ? "glib.GClosure" : "glib.GBoxed");
  mxDestroyArray(args[0]);
  mxDestroyArray(args[1]);
  mxDestroyArray(args[2]);
  return wrapper;
}

/**
 * g_matlab_mxArray_get_enum:
 * @mxValue: an #mxArray of type glib.GEnum
 *
 * Returns: the value of @mxValue
 */
guint g_matlab_mxArray_get_enum(mxArray *mxValue)
{
  g_return_val_if_fail(g_matlab_check_class(mxValue, "glib.GEnum"), 0);
  return (guint)common_get_value(mxValue);  
}

/*
 * g_matlab_mxArray_from_enum:
 * @type: the #GType of @data
 * @data: an enum
 *
 * Returns: an object of type glib.GEnum encapsulating @data
 */
mxArray * g_matlab_mxArray_from_enum(GType type, guint value)
{
  // TODO: Support non-registered enums
  g_return_val_if_fail(g_type_is_a(type, G_TYPE_ENUM), NULL);
  return common_create(type, (gpointer)value, "glib.GEnum");
}

/**
 * g_matlab_mxArray_get_flags:
 * @mxValue: an #mxArray of type glib.GFlags
 *
 * Returns: the value of @mxValue
 */
guint g_matlab_mxArray_get_flags(mxArray *mxValue)
{
  g_return_val_if_fail(g_matlab_check_class(mxValue, "glib.GFlags"), 0);
  return (guint)common_get_value(mxValue);  
}

/**
 * g_matlab_mxArray_from_flags:
 * @type: the #GType of @data
 * @data: flags
 *
 * Returns: an object of type glib.GFlags encapsulating @data
 */
mxArray * g_matlab_mxArray_from_flags(GType type, guint value)
{
  // TODO: Support non-registered flags
  g_return_val_if_fail(g_type_is_a(type, G_TYPE_FLAGS), NULL);
  return common_create(type, (gpointer)value, "glib.GFlags");
}

/**
 * g_matlab_mxArray_get_object:
 * @mxValue: an #mxArray of type glib.GObject
 *
 * Calls g_object_matlab_ensure() on object, and the increased reference is
 * returned to the caller.
 *
 * Returns: (transfer full): the #GObject contents of @mxValue
 */
GObject * g_matlab_mxArray_get_object(mxArray *mxValue)
{
  g_return_val_if_fail(g_matlab_check_class(mxValue, "glib.GObject"), NULL);
  GObject *obj = common_get_value(mxValue);
  if (obj == NULL) return NULL;
  g_object_matlab_ensure(obj, mxValue);	  // Increases ref count
  return obj;
}

/**
 * g_matlab_check_class:
 * @obj: the #mxArray of a matlab object
 * @cls: the name of the class to check
 *
 * Returns: %TRUE if @obj is derived from @cls
 */
gboolean g_matlab_check_class(mxArray *obj, const gchar *cls)
{
  if (mxIsClass(obj, cls)) return TRUE;
  
  mxArray *args[3] = { obj, g_matlab_mxArray_from_utf8(cls), NULL };
  mexCallMATLAB(1, args+2, 2, args, "isa");
  gboolean result = mxGetLogicals(args[2])[0];
  mxDestroyArray(args[1]);
  return result;
}

/**
 * g_value_from_mxArray:
 * @value: (out caller-allocates) (transfer full): an unset #GValue
 * @mxValue: the #mxArray to set the value to
 *
 * Sets @value = @mxValue.  Cell arrays will be converted to GValueArray.
 * Non-elemental arrays (n>1) of matlab objects must be cell arrays.
 *
 * Note: Uses g_matlab_escape_add(), so caller must invoke
 * g_matlab_escape_init() and follow up with g_matlab_escape_clear().
 */
void g_value_from_mxArray(GValue *value, mxArray *mxValue)
{
  mxClassID mxType;
  GValueArray *array;
  GValue *dst, tmp = {0, };
  gpointer data;
  int n, i;
  
  g_return_if_fail(mxValue != NULL);
  if (mxIsEmpty(mxValue)) g_matlab_error("gmatlab:badArray", "g_value_from_mxArray: Empty array");
  mxType = mxGetClassID(mxValue);
  n = mxGetNumberOfElements(mxValue);

  if (mxType == mxCHAR_CLASS)
  {
    gchar *str = g_matlab_mxArray_get_utf8(mxValue);
    if (str[0] != '\0' && str[1] == '\0')
    {
      g_value_init(value, G_TYPE_CHAR);
      g_value_set_char(value, str[0]);
      g_free(str);
    } else {
      g_value_init(value, G_TYPE_STRING);
      g_value_take_string(value, str);
    }
    return;
  }

  // Initialize the value
  if (n > 1 || mxType == mxCELL_CLASS)
  {
    g_value_init(value, G_TYPE_VALUE_ARRAY);
    array = g_value_array_new(n);
    g_matlab_escape_add((GHookFunc)g_value_array_free, array);
    dst = &tmp;
  }
  else  dst = value;
  
  if (mxType == mxCELL_CLASS)
  {
    for (i=0; i < n; i++)
    {
      g_value_from_mxArray(dst, mxGetCell(mxValue, i));
      g_value_array_append(array, dst);
      g_value_unset(dst);
    }
    g_value_take_boxed(value, array);
    g_matlab_escape_clear_one(array);
    return;
  }
  
  switch (mxType)
  {
    case mxLOGICAL_CLASS:
      g_value_init(dst, G_TYPE_BOOLEAN);	break;
    case mxDOUBLE_CLASS:
      g_value_init(dst, G_TYPE_DOUBLE);		break;
    case mxSINGLE_CLASS:
      g_value_init(dst, G_TYPE_FLOAT);		break;
    case mxINT8_CLASS:
    case mxINT16_CLASS:
    case mxINT32_CLASS:
      g_value_init(dst, G_TYPE_INT);		break;
    case mxUINT8_CLASS:
    case mxUINT16_CLASS:
    case mxUINT32_CLASS:
      g_value_init(dst, G_TYPE_UINT);		break;
    case mxINT64_CLASS:
      g_value_init(dst, G_TYPE_INT64);		break;
    case mxUINT64_CLASS:
      g_value_init(dst, G_TYPE_UINT64);		break;

    default:
      if (n > 1) g_matlab_error("gmatlab:badArray", "g_value_from_mxArray: Non-basic arrays must be cell arrays");
  }

  // Copy the value
  data = mxGetData(mxValue);
  for (i=0; i < n; i++)
  {
    switch (mxType)
    {
      case mxLOGICAL_CLASS:
        g_value_set_boolean(value, ((mxLogical*)data)[i]);	break;
      case mxDOUBLE_CLASS:
        g_value_set_double(value, ((gdouble*)data)[i]);		break;
      case mxSINGLE_CLASS:
        g_value_set_float(value, ((gfloat*)data)[i]);		break;
      case mxINT8_CLASS:
        g_value_set_int(value, ((gint8*)data)[i]);		break;
      case mxINT16_CLASS:
        g_value_set_int(value, ((gint16*)data)[i]);		break;
      case mxINT32_CLASS:
        g_value_set_int(value, ((gint32*)data)[i]);		break;
      case mxUINT8_CLASS:
        g_value_set_uint(value, ((guint8*)data)[i]);		break;
      case mxUINT16_CLASS:
        g_value_set_uint(value, ((guint16*)data)[i]);		break;
      case mxUINT32_CLASS:
        g_value_set_uint(value, ((guint32*)data)[i]);		break;
      case mxINT64_CLASS:
        g_value_set_int64(value, ((gint64*)data)[i]);		break;
      case mxUINT64_CLASS:
        g_value_set_uint64(value, ((guint64*)data)[i]);		break;

      case mxFUNCTION_CLASS:
      default:				  // Note: n == 1
        if (g_matlab_check_class(mxValue, "glib.GObject"))
        {
          GObject *obj = g_matlab_mxArray_get_object(mxValue);
          g_value_init(dst, obj ? G_OBJECT_TYPE(obj) : G_TYPE_OBJECT);
          g_value_take_object(dst, obj);
        }
        else if (g_matlab_check_class(mxValue, "glib.GType"))
        {
          g_value_init(dst, G_TYPE_GTYPE);
          g_value_set_gtype(dst, g_matlab_mxArray_get_gtype(mxValue));
        }
        else if (g_matlab_check_class(mxValue, "glib.GBoxed"))
        {
          GType gtype = g_matlab_mxArray_common_get_gtype(mxValue);
          gpointer ptr = g_matlab_mxArray_get_boxed(mxValue);
          if (!gtype) g_matlab_error("gmatlab:unsupported", "g_value_from_mxArray: Type system does not know about this struct. Refused.");
          g_value_init(dst, gtype);
          if (G_TYPE_IS_PARAM(gtype))
            g_value_set_param(dst, ptr);
          else if (G_TYPE_IS_BOXED(gtype))
            g_value_set_boxed(dst, ptr);
          else g_matlab_error("gmatlab:systemError", "g_value_from_mxArray: Unrecognized GBoxed.gtype %s", g_type_name(gtype));
        }
        else if (g_matlab_check_class(mxValue, "glib.GEnum"))
        {
          g_value_init(dst, g_matlab_mxArray_common_get_gtype(mxValue));
          g_value_set_enum(dst, g_matlab_mxArray_get_enum(mxValue));
        }
        else if (g_matlab_check_class(mxValue, "glib.GFlags"))
        {
          g_value_init(dst, g_matlab_mxArray_common_get_gtype(mxValue));
          g_value_set_flags(dst, g_matlab_mxArray_get_flags(mxValue));
        }
        else
          g_matlab_error("gmatlab:unsupported", "g_value_from_mxArray: Unsupported class %s", mxGetClassName(mxValue));
    }
    if (n > 1)
    {
      g_value_array_append(array, dst);
      g_value_unset(dst);
    }
  }
  if (n > 1)
  {
    g_value_take_boxed(value, array);
    g_matlab_escape_clear_one(array);
  }
}

/**
 * g_value_to_mxArray:
 * @value: (transfer none): a set #GValue
 *
 * Converts @value to a new #mxArray (column vector).  If @value is a
 * %G_TYPE_VALUE_ARRAY and not all the values have the same type or the items
 * have a non-basic type, uses a cell array.  Cell arrays are created as row 
 * vectors.  Arrays of strings are returned as cell arrays.
 *
 * Returns: the converted @value
 */
mxArray * g_value_to_mxArray(GValue *value)
{
  mxArray *mxValue = NULL;
  GValueArray *array;
  GValue *src = value;
  GType type = G_VALUE_TYPE(value);
  gboolean cellArray = FALSE;
  gpointer data = NULL;
  mwSize n = 1;
  int i;
  g_return_val_if_fail(G_IS_VALUE(value), NULL);

  if (type == G_TYPE_VALUE_ARRAY)
  {
    array = g_value_get_boxed(value);
    n = array->n_values;
    src = array->values;
    if (n > 0) type = G_VALUE_TYPE(array->values);
    for (i=1; i < n; i++)
      if (G_VALUE_TYPE(array->values+i) != type)
      {
        cellArray = TRUE;
        break;
      }
  }

  // Initialize the array
  if (cellArray)
    mxValue = mxCreateCellMatrix(1, n);
  else
    switch (G_VALUE_TYPE(src))
    {
      case G_TYPE_CHAR:
      case G_TYPE_UCHAR:
        mxValue = mxCreateCharArray(1, &n);		break;
      case G_TYPE_BOOLEAN:
        mxValue = mxCreateLogicalMatrix(n, 1);		break;
      case G_TYPE_INT:
        mxValue = mxCreateNumericMatrix(n, 1, MX_GINT, mxREAL);  break;
      case G_TYPE_UINT:
        mxValue = mxCreateNumericMatrix(n, 1, MX_GUINT, mxREAL);  break;
      case G_TYPE_LONG:
        mxValue = mxCreateNumericMatrix(n, 1, MX_GLONG, mxREAL);  break;
      case G_TYPE_ULONG:
        mxValue = mxCreateNumericMatrix(n, 1, MX_GULONG, mxREAL);  break;
      case G_TYPE_INT64:
        mxValue = mxCreateNumericMatrix(n, 1, mxINT64_CLASS, mxREAL);
        break;
      case G_TYPE_UINT64:
        mxValue = mxCreateNumericMatrix(n, 1, mxUINT64_CLASS, mxREAL);
        break;
      case G_TYPE_FLOAT:
        mxValue = mxCreateNumericMatrix(n, 1, mxSINGLE_CLASS, mxREAL);
        break;
      case G_TYPE_DOUBLE:
        mxValue = mxCreateNumericMatrix(n, 1, mxDOUBLE_CLASS, mxREAL);
        break;
      case G_TYPE_STRING:
        if (n > 1)
        {
          mxValue = mxCreateCellMatrix(1, n);
          cellArray = TRUE;
        } else
          return g_matlab_mxArray_from_utf8(g_value_get_string(value));
        break;
        
      case G_TYPE_POINTER:
        mxValue = mxCreateNumericMatrix(n, 1, MX_GPOINTER, mxREAL);
        break;

      default:
        if (n > 1)
        {
          cellArray = TRUE;
          mxValue = mxCreateCellMatrix(1, n);
        }
    }
  if (mxValue) data = mxGetData(mxValue);

  // Convert value to mxArray
  for (i=0; i < n; i++, src++)
    if (cellArray)
      mxSetCell(mxValue, i, g_value_to_mxArray(src));
    else
      switch (G_VALUE_TYPE(src))
      {
        case G_TYPE_CHAR:
          ((mxChar*)data)[i] = g_value_get_char(src);		break;
        case G_TYPE_UCHAR:
          ((mxChar*)data)[i] = g_value_get_uchar(src);		break;
        case G_TYPE_BOOLEAN:
          ((mxLogical*)data)[i] = g_value_get_boolean(src);	break;
        case G_TYPE_INT:
          ((gint*)data)[i] = g_value_get_int(src);		break;
        case G_TYPE_UINT:
          ((guint*)data)[i] = g_value_get_uint(src);		break;
        case G_TYPE_LONG:
          ((glong*)data)[i] = g_value_get_long(src);		break;
        case G_TYPE_ULONG:
          ((gulong*)data)[i] = g_value_get_ulong(src);		break;
        case G_TYPE_INT64:
          ((gint64*)data)[i] = g_value_get_int64(src);		break;
        case G_TYPE_UINT64:
          ((guint64*)data)[i] = g_value_get_uint64(src);	break;
        case G_TYPE_FLOAT:
          ((gfloat*)data)[i] = g_value_get_float(src);		break;
        case G_TYPE_DOUBLE:
          ((gdouble*)data)[i] = g_value_get_double(src);	break;
        case G_TYPE_POINTER:
          ((gpointer*)data)[i] = g_value_get_pointer(src);	break;
          break;

        default:  // Note: n == 1
          if (G_VALUE_HOLDS_OBJECT(src))
            mxValue = g_object_matlab_wrap(g_value_get_object(src));
          else if (G_VALUE_HOLDS_GTYPE(src))
            mxValue = g_matlab_mxArray_from_gtype(g_value_get_gtype(src));
          else if (G_VALUE_HOLDS_BOXED(src))
            mxValue = g_matlab_mxArray_from_boxed(G_VALUE_TYPE(src), g_value_get_boxed(src));
          else if (G_VALUE_HOLDS_PARAM(src))
            mxValue = g_matlab_mxArray_from_boxed(G_VALUE_TYPE(src), g_value_get_param(src));
          else if (G_VALUE_HOLDS_ENUM(src))
            mxValue = g_matlab_mxArray_from_enum(G_VALUE_TYPE(src), g_value_get_enum(src));
          else if (G_VALUE_HOLDS_FLAGS(src))
            mxValue = g_matlab_mxArray_from_flags(G_VALUE_TYPE(src), g_value_get_flags(src));
          else g_matlab_error("gmatlab:unsupported", "g_value_to_mxArray: Unsupported value type %s (%d)", g_type_name(G_VALUE_TYPE(src)), G_VALUE_TYPE(src));
      }

  return mxValue;
}

static gboolean arg_matlab_ptr_compatible(GITypeTag tag, mxClassID type)
{
  switch (tag)
  {
    case GI_TYPE_TAG_BOOLEAN:
      switch (type)
      {
        case mxLOGICAL_CLASS: return sizeof(gboolean) == sizeof(mxLogical);
        case mxCHAR_CLASS: return sizeof(gboolean) == sizeof(mxChar);
        case mxINT8_CLASS:
        case mxUINT8_CLASS: return sizeof(gboolean) == 1;
        case mxINT16_CLASS:
        case mxUINT16_CLASS: return sizeof(gboolean) == 2;
        case mxINT32_CLASS:
        case mxUINT32_CLASS: return sizeof(gboolean) == 4;
        case mxINT64_CLASS:
        case mxUINT64_CLASS: return sizeof(gboolean) == 8;
        default: return FALSE;
      }
    case GI_TYPE_TAG_INT8:
    case GI_TYPE_TAG_UINT8:
      switch (type)
      {
        case mxLOGICAL_CLASS: return sizeof(mxLogical) == 1;
        case mxCHAR_CLASS: return sizeof(mxChar) == 1;
        case mxINT8_CLASS:
        case mxUINT8_CLASS: return TRUE;
        default: return FALSE;
      }
    case GI_TYPE_TAG_INT16:
    case GI_TYPE_TAG_UINT16:
      switch (type)
      {
        case mxLOGICAL_CLASS: return sizeof(mxLogical) == 2;
        case mxCHAR_CLASS: return sizeof(mxChar) == 2;
        case mxINT16_CLASS:
        case mxUINT16_CLASS: return TRUE;
        default: return FALSE;
      }
    case GI_TYPE_TAG_INT32:
    case GI_TYPE_TAG_UINT32:
      switch (type)
      {
        case mxLOGICAL_CLASS: return sizeof(mxLogical) == 4;
        case mxCHAR_CLASS: return sizeof(mxChar) == 4;
        case mxINT32_CLASS:
        case mxUINT32_CLASS: return TRUE;
        default: return FALSE;
      }
    case GI_TYPE_TAG_INT64:
    case GI_TYPE_TAG_UINT64:
      switch (type)
      {
        case mxLOGICAL_CLASS: return sizeof(mxLogical) == 8;
        case mxCHAR_CLASS: return sizeof(mxChar) == 8;
        case mxINT64_CLASS:
        case mxUINT64_CLASS: return TRUE;
        default: return FALSE;
      }
    case GI_TYPE_TAG_FLOAT:  return type == mxSINGLE_CLASS;
    case GI_TYPE_TAG_DOUBLE: return type == mxDOUBLE_CLASS;
    default: return FALSE;
  }
}

static void dispose_array(gpointer array)
{
  g_array_free(array, TRUE); 
}

static void dispose_ptr_array(gpointer array)
{
  g_ptr_array_free(array, TRUE); 
}

typedef struct { GType type; gpointer ptr; } BoxClosure;

static void dispose_boxed(gpointer ptr)
{
  BoxClosure *boxed = ptr;
  g_boxed_free(boxed->type, boxed->ptr);
}

/**
 * g_arg_from_mxArray:
 * @arg (out caller-allocates) (transfer full): the argument to set
 * @type: the #GITypeInfo describing the argument
 * @mxValue: the #mxArray to set the value from
 * @transfer: argument transfer information
 * @n: argument number (for error reporting)
 *
 * Sets @arg = @mxValue, if type is convertible.
 *
 * Non-elemental (n>1) arrays of matlab objects or strings
 * <emphasis>must</emphasis be cell arrays.  All other types (scalars or 
 * arrays) may <emphasis>not</emphasis> be cell arrays.

 * String arrays <emphasis>must</emphasis> be submitted as matlab cell
 * arrays.  <emphasis>Arrays</emphasis> of %GI_TYPE_TAG_INTERFACE (objects
 * and boxed types) of %GI_TYPE_TAG_GTYPE <emphasis>may</emphasis> be
 * submitted as matlab cell arrays.  All other types (scalars or arrays) may
 * <emphasis>not</emphasis> be submitted as a matlab cell array.
 *
 * This function uses g_matlab_escape_add(), so caller must ensure that
 * g_matlab_escape_init() has been called and follow up with
 * g_matlab_escape_clear().
 */
void g_arg_from_mxArray(GArgument *arg, GIBaseInfo *type,
                        mxArray *mxValue, GITransfer transfer, int n)
{
  GITypeTag tag, itemTag;
  GIArrayType arrayType;
  GIInfoType interfaceType;
  mxClassID mxType = mxUNKNOWN_CLASS;
  gboolean cellArray = FALSE;
  gboolean zeroTerminated;
  GArgument tmp, *dst = arg;
  gpointer data;
  int i, num = 1;

  if (GI_IS_TYPE_INFO(type))
    itemTag = tag = g_type_info_get_tag(type);
  else
    itemTag = tag = GI_TYPE_TAG_INTERFACE;
//  TRACE("g_arg_from_mxArray[%d]: tag is %d (type %s typeinfo)", n, tag, GI_IS_TYPE_INFO(type) ? "is": "is not");
  
  g_return_if_fail(tag != GI_TYPE_TAG_VOID);
  g_return_if_fail(mxValue != NULL);
  if (mxIsEmpty(mxValue)) g_matlab_error("gmatlab:badArray", "g_arg_from_mxArray: Argument %d is empty", n);
  mxType = mxGetClassID(mxValue);
  num = mxGetNumberOfElements(mxValue);

  if (tag == GI_TYPE_TAG_ARRAY ||
      tag == GI_TYPE_TAG_GLIST || tag == GI_TYPE_TAG_GSLIST)
  {
    if (tag == GI_TYPE_TAG_ARRAY)
    {
      arrayType = g_type_info_get_array_type(type);
      zeroTerminated = g_type_info_is_zero_terminated(type);
    }
    type = g_type_info_get_param_type(type, 0);
    g_matlab_cleanup_add((GHookFunc)g_base_info_unref, type);
    itemTag = g_type_info_get_tag(type);
    cellArray = (itemTag >= GI_TYPE_TAG_GTYPE);

    arg->v_pointer = NULL;
    dst = &tmp;
  } else if (num > 1 &&
             !(tag == GI_TYPE_TAG_UTF8 || tag == GI_TYPE_TAG_FILENAME))
    g_matlab_error("gmatlab:badArray", "g_arg_from_mxArray: Unexpected non-scalar processing argument %d", n);

  if (cellArray && !mxIsCell(mxValue))
    g_matlab_error("gmatlab:badArray", "g_arg_from_mxArray: Non-basic arrays must be matlab cell arrays");
  if (mxIsCell(mxValue) && !cellArray)
    g_matlab_error("gmatlab:badArray", "g_arg_from_mxArray: Cell arrays permitted only for GI_TYPE_TAG_INSTANCE, GI_TYPE_TAG_GTYPE, and string types.");

//  TRACE("g_arg_from_mxArray: itemTag is %d", itemTag);
  if (itemTag == GI_TYPE_TAG_INTERFACE)
  {
//    TRACE("g_arg_from_mxArray[%d]: fetching interface", n);
    if (GI_IS_TYPE_INFO(type))
    {
      type = g_type_info_get_interface(type);
      g_matlab_cleanup_add((GHookFunc)g_base_info_unref, type);
    }  // Otherwise the interface was passed to the function directly
    interfaceType = g_base_info_get_type(type);
//    TRACE("g_arg_from_mxArray[%d]: interface type is %d", n, interfaceType);
  }
  
  if (tag == GI_TYPE_TAG_ARRAY)
  {
    if (arrayType == GI_ARRAY_TYPE_C && transfer == GI_TRANSFER_NOTHING &&
        arg_matlab_ptr_compatible(itemTag, mxType))
    {
      arg->v_pointer = mxGetData(mxValue);
      return;
    }
    
    // Prepare space for array
    if (arrayType == GI_ARRAY_TYPE_PTR_ARRAY)
      arg->v_pointer = g_ptr_array_sized_new(num);
    else
    {
      guint argSize;
      switch (GI_IS_ENUM_INFO(type)
              ? g_enum_info_get_storage_type(type)
              : itemTag)
      {
        case GI_TYPE_TAG_BOOLEAN:	argSize = sizeof(gboolean);	break;
        case GI_TYPE_TAG_INT8:
        case GI_TYPE_TAG_UINT8:     argSize = 1;			break;
        case GI_TYPE_TAG_INT16:
        case GI_TYPE_TAG_UINT16:    argSize = 2;			break;
        case GI_TYPE_TAG_INT32:
        case GI_TYPE_TAG_UINT32:    argSize = 4;			break;
        case GI_TYPE_TAG_INT64:
        case GI_TYPE_TAG_UINT64:    argSize = 8;			break;
        case GI_TYPE_TAG_FLOAT:     argSize = sizeof(gfloat);	break;
        case GI_TYPE_TAG_DOUBLE:    argSize = sizeof(gdouble);	break;
        case GI_TYPE_TAG_GTYPE:     argSize = sizeof(GType);	break;
        case GI_TYPE_TAG_UTF8:
        case GI_TYPE_TAG_FILENAME:
        default:                    argSize = GLIB_SIZEOF_VOID_P;
      }
      arg->v_pointer = g_array_sized_new(zeroTerminated, FALSE, argSize, num);
    }
    if (transfer == GI_TRANSFER_NOTHING)
      g_matlab_cleanup_add(arrayType == GI_ARRAY_TYPE_PTR_ARRAY
                           ? dispose_ptr_array : dispose_array,
                           arg->v_pointer);
    else
      g_matlab_escape_add(arrayType == GI_ARRAY_TYPE_PTR_ARRAY
                          ? dispose_ptr_array : dispose_array,
                          arg->v_pointer);
  }
  
  data = mxGetData(mxValue);
  for (i=0; i < num; i++)
  {
    if (cellArray)
      g_arg_from_mxArray(dst, type, mxGetCell(mxValue, i),
                         transfer == GI_TRANSFER_CONTAINER ?
                         GI_TRANSFER_NOTHING : transfer, n);
    else switch (itemTag)
    {
      case GI_TYPE_TAG_BOOLEAN:
      case GI_TYPE_TAG_INT8:
      case GI_TYPE_TAG_UINT8:
      case GI_TYPE_TAG_INT16:
      case GI_TYPE_TAG_UINT16:
      case GI_TYPE_TAG_INT32:
      case GI_TYPE_TAG_UINT32:
      case GI_TYPE_TAG_INT64:
      case GI_TYPE_TAG_UINT64:
      case GI_TYPE_TAG_FLOAT:
      case GI_TYPE_TAG_DOUBLE:
        // Recall: cellArray==true only if non-basic type
        if (!arg_set_from_mxArray(dst, itemTag, data, mxType, i))
          g_matlab_error("gmatlab:badType", "g_arg_from_mxArray: Argument %d expects %s, not %s", n, g_type_tag_to_string(itemTag), mxGetClassName(mxValue));
        break;
		
      // Note: for everything following, num == 1 (otherwise cellArray)
      
      case GI_TYPE_TAG_UTF8:
      case GI_TYPE_TAG_FILENAME:
        if (mxType != mxCHAR_CLASS)
          g_matlab_error("gmatlab:badType", "g_arg_from_mxArray: Argument %d must be character, not %s", n, mxGetClassName(mxValue));
        dst->v_pointer = g_matlab_mxArray_get_utf8(mxValue);
        if (transfer != GI_TRANSFER_EVERYTHING)
          g_matlab_cleanup_add(g_free, dst->v_pointer);
        else
          g_matlab_escape_add(g_free, dst->v_pointer);
        // num is string length, not number of strings
        // so we skip out of the for loop early here
        return;

      case GI_TYPE_TAG_GTYPE:
        if (!g_matlab_check_class(mxValue, "glib.GType"))
          g_matlab_error("gmatlab:badType", "g_arg_from_mxArray: Argument %d must be GType, not %s", n, mxGetClassName(mxValue));
        dst->v_long = g_matlab_mxArray_get_gtype(mxValue);
        break;
		
      case GI_TYPE_TAG_ERROR:		// GError is boxed
        if (!g_matlab_check_class(mxValue, "glib.GBoxed") ||
            g_matlab_mxArray_common_get_gtype(mxValue) != G_TYPE_ERROR)
          g_matlab_error("gmatlab:badType", "g_arg_from_mxArray: Argument %d expects GError", n);
        dst->v_pointer = g_matlab_mxArray_get_boxed(mxValue);
        if (transfer == GI_TRANSFER_EVERYTHING)
        {
          BoxClosure *box = g_new(BoxClosure, 1);
          box->type = g_matlab_mxArray_common_get_gtype(mxValue);
          box->ptr = dst->v_pointer = g_boxed_copy(box->type, dst->v_pointer);
          g_matlab_escape_add(dispose_boxed, box);
          g_matlab_cleanup_add(g_free, box);
        }
        break;
		
      case GI_TYPE_TAG_INTERFACE:
        switch (interfaceType)
        {
          case GI_INFO_TYPE_OBJECT:
            if (!g_matlab_check_class(mxValue, "glib.GObject"))
              g_matlab_error("gmatlab:badType", "g_arg_from_mxArray: Argument %d expects GObject, not %s", n, mxGetClassName(mxValue));
            dst->v_pointer = g_matlab_mxArray_get_object(mxValue);
            if (dst->v_pointer)
            {
              if (transfer == GI_TRANSFER_EVERYTHING)
                g_matlab_escape_add(g_object_unref, dst->v_pointer);
              else
                g_matlab_cleanup_add(g_object_unref, dst->v_pointer);
            }
            break;
			
          case GI_INFO_TYPE_STRUCT:
            if (!g_matlab_check_class(mxValue, "glib.GBoxed"))
              g_matlab_error("gmatlab:badType", "g_arg_from_mxArray: Argument %d expects a struct, not %s", n, mxGetClassName(mxValue));
            dst->v_pointer = g_matlab_mxArray_get_boxed(mxValue);
            if (transfer == GI_TRANSFER_EVERYTHING)
            {
              if (G_TYPE_IS_PARAM(g_matlab_mxArray_common_get_gtype(mxValue)))
              {
                g_param_spec_ref(dst->v_pointer);
                g_matlab_escape_add((GHookFunc)g_param_spec_unref, dst->v_pointer);
              } else {
                dst->v_pointer = g_memdup(dst->v_pointer, g_struct_info_get_size(type));
                g_matlab_escape_add(g_free, dst->v_pointer);
              }
            }
            break;
			
          case GI_INFO_TYPE_BOXED:
            if (!g_matlab_check_class(mxValue, "glib.GBoxed"))
              g_matlab_error("gmatlab:badType", "g_arg_from_mxArray: Argument %d expects GBoxed, not %s", n, mxGetClassName(mxValue));
            dst->v_pointer = g_matlab_mxArray_get_boxed(mxValue);
            if (transfer == GI_TRANSFER_EVERYTHING)
            {
              BoxClosure *box = g_new(BoxClosure, 1);
              box->type = g_matlab_mxArray_common_get_gtype(mxValue);
              box->ptr = dst->v_pointer = g_boxed_copy(box->type, dst->v_pointer);
              g_matlab_escape_add(dispose_boxed, box);
              g_matlab_cleanup_add(g_free, box);
            }
            break;

          case GI_INFO_TYPE_ENUM:
            // Hmm, could be trouble for enum arrays if enum size < ptr size
            // and host is big-endian
            if (!g_matlab_check_class(mxValue, "glib.GEnum"))
              g_matlab_error("gmatlab:badType", "g_arg_from_mxArray: Argument %d expects GEnum, not %s", n, mxGetClassName(mxValue));
            dst->v_long = g_matlab_mxArray_get_enum(mxValue);;
            break;

          case GI_INFO_TYPE_FLAGS:
            if (!g_matlab_check_class(mxValue, "glib.GFlags"))
              g_matlab_error("gmatlab:badType", "g_arg_from_mxArray: Argument %d expects GFlags, not %s", n, mxGetClassName(mxValue));
            dst->v_long = g_matlab_mxArray_get_flags(mxValue);
            break;

          default:
            g_matlab_error("gmatlab:systemError", "g_arg_from_mxArray: What do I do with interface type %d?", interfaceType);
        }
        break;
      
      case GI_TYPE_TAG_GHASH:
        // TODO: GHasHTable argument
        g_matlab_error("gmatlab:FIXME", "g_arg_from_mxArray: FIXME: GHashTable argument");
        break;
        
      case GI_TYPE_TAG_VOID:
      default:
        g_matlab_error("gmatlab:systemError", "g_arg_from_mxArray: Unknown type tag %d", itemTag);
    }
	
    switch (tag)
    {
      case GI_TYPE_TAG_ARRAY:
        if (arrayType == GI_ARRAY_TYPE_PTR_ARRAY)
          g_ptr_array_add(arg->v_pointer, dst->v_pointer);
        else
          g_array_append_vals(arg->v_pointer, dst, 1);
        break;

        // The GList/GSList operations would be faster backwards.
      case GI_TYPE_TAG_GLIST:
        arg->v_pointer = g_list_append(arg->v_pointer, dst->v_pointer);
        break;
      case GI_TYPE_TAG_GSLIST:
        arg->v_pointer = g_slist_append(arg->v_pointer, dst->v_pointer);
        break;
      default:  break;
    }
  } // for i

  if (tag == GI_TYPE_TAG_ARRAY && arrayType == GI_ARRAY_TYPE_C)
  {
    GArray *tmp = arg->v_pointer;                // Tmp ptr to wrapper
    arg->v_pointer = tmp->data;                  // Arg gets contents
    if (transfer != GI_TRANSFER_NOTHING)
    {
      g_matlab_escape_clear_one(tmp); // Remove wrapper escape cleanup
      g_matlab_escape_add(g_free, arg->v_pointer);
      g_array_free(tmp, FALSE);       // Free the wrapper
    }  // Otherwise, wrapper and contents have g_matlab_cleanup()
  }
  
}

/**
 * g_arg_to_mxArray:
 * @arg: the argument to convert
 * @type: the argument #GITypeInfo
 *
 * Creates a new #mxArray from @arg of @type.  Arrays of non-basic types are
 * returned as cell arrays.
 *
 * Array arguments of type GI_ARRAY_TYPE_C must be converted to #GArray
 * before calling this function.
 *
 * Note: System errors during conversion could cause memory leaks for
 * transfer full values.
 *
 * Returns: a new #mxArray of the converted argument
 */
mxArray * g_arg_to_mxArray(GArgument *arg, GIBaseInfo *type,
                           GITransfer transfer)
{
  GITypeTag tag, itemTag;
  GIInfoType interfaceType;
  mxClassID mxType = mxUNKNOWN_CLASS;
  mxArray *mxValue = NULL;
  GSList *list;
  GArgument *src = arg;
  gpointer data;
  gboolean ptrArray;
  gboolean cellArray = FALSE;
  guint argSize;
  int i, num=1;

  if (GI_IS_TYPE_INFO(type))
    itemTag = tag = g_type_info_get_tag(type);
  else
    itemTag = tag = GI_TYPE_TAG_INTERFACE;

  if (tag == GI_TYPE_TAG_ARRAY)
    ptrArray = (g_type_info_get_array_type(type) == GI_ARRAY_TYPE_PTR_ARRAY);
  
  if (tag == GI_TYPE_TAG_ARRAY ||
      tag == GI_TYPE_TAG_GLIST || tag == GI_TYPE_TAG_GSLIST)
  {
    type = g_type_info_get_param_type(type, 0);
    g_matlab_cleanup_add((GHookFunc)g_base_info_unref, type);
    itemTag = g_type_info_get_tag(type);
    cellArray = (itemTag >= GI_TYPE_TAG_GTYPE);

    // Get item count
    if (tag == GI_TYPE_TAG_ARRAY)
    {
      num = ((GArray*)arg->v_pointer)->len;
      src = (GArgument*)(((GArray*)arg->v_pointer)->data);
      if (ptrArray) argSize = GLIB_SIZEOF_VOID_P;
      else argSize = g_array_get_element_size(arg->v_pointer);
    }
    else
    {
      list = arg->v_pointer;
      for (num=0; list; list = list->next) num++;
      list = arg->v_pointer;
    }
  }
	
  if (itemTag == GI_TYPE_TAG_INTERFACE)
  {
    type = g_type_info_get_interface(type);
    g_matlab_cleanup_add((GHookFunc)g_base_info_unref, type);
    interfaceType = g_base_info_get_type(type);
  }
  
  if (cellArray)
    mxValue = mxCreateCellMatrix(1, num);
  else
  {
    switch (itemTag)
    {
      case GI_TYPE_TAG_BOOLEAN:
        mxValue = mxCreateLogicalMatrix(1, num);
        mxType = mxLOGICAL_CLASS;
        break;
      case GI_TYPE_TAG_INT8:   mxType = mxINT8_CLASS;   break;
      case GI_TYPE_TAG_UINT8:  mxType = mxUINT8_CLASS;  break;
      case GI_TYPE_TAG_INT16:  mxType = mxINT16_CLASS;  break;
      case GI_TYPE_TAG_UINT16: mxType = mxUINT16_CLASS; break;
      case GI_TYPE_TAG_INT32:  mxType = mxINT32_CLASS;  break;
      case GI_TYPE_TAG_UINT32: mxType = mxUINT32_CLASS; break;
      case GI_TYPE_TAG_INT64:  mxType = mxINT64_CLASS;  break;
      case GI_TYPE_TAG_UINT64: mxType = mxUINT64_CLASS; break;
      case GI_TYPE_TAG_FLOAT:  mxType = mxSINGLE_CLASS; break;
      case GI_TYPE_TAG_DOUBLE: mxType = mxDOUBLE_CLASS; break;
      default: break;
    }
    if (mxType != mxUNKNOWN_CLASS && mxType != mxLOGICAL_CLASS)
      mxValue = mxCreateNumericMatrix(1, num, mxType, mxREAL);
    if (mxValue) data = mxGetData(mxValue);
  }

  for (i=0; i < num; i++)
  {
    switch (tag)
    {
      case GI_TYPE_TAG_GLIST:
      case GI_TYPE_TAG_GSLIST:
        src = (GArgument*)&(list->data);
        break;
      default: break;
    }
	
    if (cellArray)
      mxSetCell(mxValue, i, g_arg_to_mxArray(src, type, transfer));
    else switch (itemTag)
    {
      case GI_TYPE_TAG_BOOLEAN:
      case GI_TYPE_TAG_INT8:
      case GI_TYPE_TAG_UINT8:
      case GI_TYPE_TAG_INT16:
      case GI_TYPE_TAG_UINT16:
      case GI_TYPE_TAG_INT32:
      case GI_TYPE_TAG_UINT32:
      case GI_TYPE_TAG_INT64:
      case GI_TYPE_TAG_UINT64:
      case GI_TYPE_TAG_FLOAT:
      case GI_TYPE_TAG_DOUBLE:
        // Recall: cellArray==true only if non-basic type
        g_return_val_if_fail(arg_set_mxArray(src, itemTag, data, mxType, i), NULL);
        break;
        
      // Note: for everything following, num == 1 (otherwise cellArray)
		
      case GI_TYPE_TAG_UTF8:
      case GI_TYPE_TAG_FILENAME:
        mxValue = g_matlab_mxArray_from_utf8(arg->v_pointer ?
                                             arg->v_pointer : "");
        if (transfer == GI_TRANSFER_EVERYTHING)
          g_free(arg->v_pointer);
        break;
		
      case GI_TYPE_TAG_GTYPE:
        mxValue = g_matlab_mxArray_from_gtype(src->v_long);
        break;
		
      case GI_TYPE_TAG_ERROR:		// GError is boxed
        // mxArray_from_boxed requires its own copy
        mxValue = g_matlab_mxArray_from_boxed(G_TYPE_ERROR,
                    transfer == GI_TRANSFER_EVERYTHING ? src->v_pointer
                    : g_error_copy(src->v_pointer));
        break;
		
      case GI_TYPE_TAG_INTERFACE:
        switch (interfaceType)
        {
          case GI_INFO_TYPE_OBJECT:
            mxValue = g_object_matlab_wrap(src->v_pointer);
            if (src->v_pointer && transfer == GI_TRANSFER_EVERYTHING)
              g_object_unref(src->v_pointer);
            break;
		  
          // Is GI_INFO_TYPE_BOXED actually used for boxed types outside of glib??
          case GI_INFO_TYPE_BOXED:
          case GI_INFO_TYPE_STRUCT:
          {
            GType gtype = g_registered_type_info_get_g_type(type);
            if (G_TYPE_IS_PARAM(gtype))
            {
              mxValue = g_matlab_mxArray_from_boxed(
                          g_registered_type_info_get_g_type(type),
                                                    src->v_pointer);
              if (transfer != GI_TRANSFER_EVERYTHING)
                g_param_spec_ref(src->v_pointer);
            } else if (G_TYPE_IS_BOXED(gtype)) {
              // mxArray_from_boxed requires its own copy
//              GType gtype = g_registered_type_info_get_g_type(type);
              mxValue = g_matlab_mxArray_from_boxed(gtype,
                                                    transfer == GI_TRANSFER_EVERYTHING ? src->v_pointer
                                                    : g_boxed_copy(gtype, src->v_pointer));
            } else {
              mxArray *args[3];
              args[0] = mxCreateNumericMatrix(1, 1, MX_GLONG, mxREAL);
              ((GType*)mxGetData(args[0]))[0] = 0;
              args[1] = mxCreateNumericMatrix(1, 1, MX_GPOINTER, mxREAL);
              ((gpointer*)mxGetData(args[1]))[0] =
                          (transfer == GI_TRANSFER_EVERYTHING ? src->v_pointer
                          : g_memdup(src->v_pointer,
                                     g_struct_info_get_size(type)));
              args[2] = g_matlab_mxArray_from_giInfo(type);
              TRACE("g_arg_to_mxArray: Wrap struct 0, 0x%ld", ((gpointer*)mxGetData(args[1]))[0]);
              mexCallMATLAB(1, &mxValue, 3, args, "glib.GBoxed");
              mxDestroyArray(args[0]);
              mxDestroyArray(args[1]);
            }
          }
            break;
		  
          case GI_INFO_TYPE_ENUM:
            // Hmm, could be trouble for enum arrays if enum size < int size
            // and host is big-endian
            mxValue = g_matlab_mxArray_from_enum(
                        g_registered_type_info_get_g_type(type), arg->v_uint);
            break;
		  
          case GI_INFO_TYPE_FLAGS:
            mxValue = g_matlab_mxArray_from_flags(
                        g_registered_type_info_get_g_type(type), arg->v_uint);
            break;
		  
          default:
            g_matlab_error("gmatlab:systemError", "g_arg_to_mxArray: What do I do with interface type %d?", interfaceType);
        }
        break;
		
      case GI_TYPE_TAG_GHASH:
        // TODO: GHashTable argument
        g_matlab_error("gmatlab:FIXME", "g_arg_to_mxArray: FIXME: GHashTable argument");
        break;
        
      case GI_TYPE_TAG_VOID:
      default:
        g_matlab_error("gmatlab:systemError", "g_arg_to_mxArray: Unknown type tag %d", itemTag);
    }
  
    switch (tag)
    {
      case GI_TYPE_TAG_ARRAY:
        src = (GArgument*)((gpointer)src + argSize);
        break;
      case GI_TYPE_TAG_GLIST:
      case GI_TYPE_TAG_GSLIST:
        list = list->next;
        break;
      default: break;
    }
  }
  
  if (transfer != GI_TRANSFER_NOTHING)
    switch (tag)
    {
      // All elements were copied, so we can free segment.
      // TODO: What if ptr array has free func?
      case GI_TYPE_TAG_ARRAY:
        if (ptrArray) g_ptr_array_free(arg->v_pointer, TRUE);
        else g_array_free(arg->v_pointer, TRUE);
        break;
      case GI_TYPE_TAG_GLIST:
        g_list_free(arg->v_pointer);
        break;
      case GI_TYPE_TAG_GSLIST:
        g_slist_free(arg->v_pointer);
        break;
      default: break;
    }
  
  return mxValue;
}

gboolean arg_set_from_mxArray(GArgument *arg, GITypeTag type, 
                              gpointer data, mxClassID mxType, guint j)
{
  g_return_val_if_fail(arg && data, FALSE);
  g_return_val_if_fail(type < GI_TYPE_TAG_GTYPE, FALSE);
  g_return_val_if_fail(type != GI_TYPE_TAG_VOID, FALSE);
  switch (mxType)
  {
    case mxLOGICAL_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          arg->v_boolean = ((mxLogical*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT8:
          arg->v_int8 = ((mxLogical*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT8:
          arg->v_uint8 = ((mxLogical*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT16:
          arg->v_int16 = ((mxLogical*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT16:
          arg->v_uint16 = ((mxLogical*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT32:
          arg->v_int32 = ((mxLogical*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT32:
          arg->v_uint32 = ((mxLogical*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT64:
          arg->v_int64 = ((mxLogical*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT64:
          arg->v_uint64 = ((mxLogical*)data)[j];	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          arg->v_float = ((mxLogical*)data)[j];	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          arg->v_double = ((mxLogical*)data)[j];	return TRUE;
        default:
          return FALSE;
      }
      
    case mxCHAR_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          arg->v_boolean = ((mxChar*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT8:
          arg->v_int8 = ((mxChar*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT8:
          arg->v_uint8 = ((mxChar*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT16:
          arg->v_int16 = ((mxChar*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT16:
          arg->v_uint16 = ((mxChar*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT32:
          arg->v_int32 = ((mxChar*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT32:
          arg->v_uint32 = ((mxChar*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT64:
          arg->v_int64 = ((mxChar*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT64:
          arg->v_uint64 = ((mxChar*)data)[j];	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          arg->v_float = ((mxChar*)data)[j];	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          arg->v_double = ((mxChar*)data)[j];	return TRUE;
        default:
          return FALSE;
      }

    case mxDOUBLE_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          arg->v_boolean = ((gdouble*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT8:
          arg->v_int8 = ((gdouble*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT8:
          arg->v_uint8 = ((gdouble*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT16:
          arg->v_int16 = ((gdouble*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT16:
          arg->v_uint16 = ((gdouble*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT32:
          arg->v_int32 = ((gdouble*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT32:
          arg->v_uint32 = ((gdouble*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT64:
          arg->v_int64 = ((gdouble*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT64:
          arg->v_uint64 = ((gdouble*)data)[j];	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          arg->v_float = ((gdouble*)data)[j];	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          arg->v_double = ((gdouble*)data)[j];	return TRUE;
        default:
          return FALSE;
      }

    case mxSINGLE_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          arg->v_boolean = ((gfloat*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT8:
          arg->v_int8 = ((gfloat*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT8:
          arg->v_uint8 = ((gfloat*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT16:
          arg->v_int16 = ((gfloat*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT16:
          arg->v_uint16 = ((gfloat*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT32:
          arg->v_int32 = ((gfloat*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT32:
          arg->v_uint32 = ((gfloat*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT64:
          arg->v_int64 = ((gfloat*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT64:
          arg->v_uint64 = ((gfloat*)data)[j];	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          arg->v_float = ((gfloat*)data)[j];	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          arg->v_double = ((gfloat*)data)[j];	return TRUE;
        default:
          return FALSE;
      }

    case mxINT8_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          arg->v_boolean = ((gint8*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT8:
          arg->v_int8 = ((gint8*)data)[j];		return TRUE;
        case GI_TYPE_TAG_UINT8:
          arg->v_uint8 = ((gint8*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT16:
          arg->v_int16 = ((gint8*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT16:
          arg->v_uint16 = ((gint8*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT32:
          arg->v_int32 = ((gint8*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT32:
          arg->v_uint32 = ((gint8*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT64:
          arg->v_int64 = ((gint8*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT64:
          arg->v_uint64 = ((gint8*)data)[j];	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          arg->v_float = ((gint8*)data)[j];	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          arg->v_double = ((gint8*)data)[j];	return TRUE;
        default:
          return FALSE;
      }

    case mxINT16_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          arg->v_boolean = ((gint16*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT8:
          arg->v_int8 = ((gint16*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT8:
          arg->v_uint8 = ((gint16*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT16:
          arg->v_int16 = ((gint16*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT16:
          arg->v_uint16 = ((gint16*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT32:
          arg->v_int32 = ((gint16*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT32:
          arg->v_uint32 = ((gint16*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT64:
          arg->v_int64 = ((gint16*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT64:
          arg->v_uint64 = ((gint16*)data)[j];	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          arg->v_float = ((gint16*)data)[j];	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          arg->v_double = ((gint16*)data)[j];	return TRUE;
        default:
          return FALSE;
      }

    case mxINT32_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          arg->v_boolean = ((gint32*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT8:
          arg->v_int8 = ((gint32*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT8:
          arg->v_uint8 = ((gint32*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT16:
          arg->v_int16 = ((gint32*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT16:
          arg->v_uint16 = ((gint32*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT32:
          arg->v_int32 = ((gint32*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT32:
          arg->v_uint32 = ((gint32*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT64:
          arg->v_int64 = ((gint32*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT64:
          arg->v_uint64 = ((gint32*)data)[j];	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          arg->v_float = ((gint32*)data)[j];	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          arg->v_double = ((gint32*)data)[j];	return TRUE;
/*
#if GLIB_SIZEOF_LONG == 4
        case GI_TYPE_TAG_GTYPE:
          ((GType*)arg)[i] = ((gint32*)data)[j];	return TRUE;
#endif
#if GLIB_SIZEOF_VOID_P == 4
        case GI_TYPE_TAG_INTERFACE:
          ((gpointer*)arg)[i] = ((gint32*)data)[j];	return TRUE;
#endif
*/
        default:
          return FALSE;
      }

    case mxINT64_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          arg->v_boolean = ((gint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT8:
          arg->v_int8 = ((gint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT8:
          arg->v_uint8 = ((gint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT16:
          arg->v_int16 = ((gint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT16:
          arg->v_uint16 = ((gint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT32:
          arg->v_int32 = ((gint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT32:
          arg->v_uint32 = ((gint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT64:
          arg->v_int64 = ((gint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT64:
          arg->v_uint64 = ((gint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          arg->v_float = ((gint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          arg->v_double = ((gint64*)data)[j];	return TRUE;
/*
        case GI_TYPE_TAG_GTYPE:
          ((GType*)arg)[i] = ((gint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INTERFACE:
          ((gpointer*)arg)[i] = ((gint64*)data)[j];	return TRUE;
 */
        default:
          return FALSE;
      }

    case mxUINT8_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          arg->v_boolean = ((guint8*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT8:
          arg->v_int8 = ((guint8*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT8:
          arg->v_uint8 = ((guint8*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT16:
          arg->v_int16 = ((guint8*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT16:
          arg->v_uint16 = ((guint8*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT32:
          arg->v_int32 = ((guint8*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT32:
          arg->v_uint32 = ((guint8*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT64:
          arg->v_int64 = ((guint8*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT64:
          arg->v_uint64 = ((guint8*)data)[j];	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          arg->v_float = ((guint8*)data)[j];	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          arg->v_double = ((guint8*)data)[j];	return TRUE;
        default:
          return FALSE;
      }

    case mxUINT16_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          arg->v_boolean = ((guint16*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT8:
          arg->v_int8 = ((guint16*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT8:
          arg->v_uint8 = ((guint16*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT16:
          arg->v_int16 = ((guint16*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT16:
          arg->v_uint16 = ((guint16*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT32:
          arg->v_int32 = ((guint16*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT32:
          arg->v_uint32 = ((guint16*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT64:
          arg->v_int64 = ((guint16*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT64:
          arg->v_uint64 = ((guint16*)data)[j];	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          arg->v_float = ((guint16*)data)[j];	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          arg->v_double = ((guint16*)data)[j];	return TRUE;
        default:
          return FALSE;
      }

    case mxUINT32_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          arg->v_boolean = ((guint32*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT8:
          arg->v_int8 = ((guint32*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT8:
          arg->v_uint8 = ((guint32*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT16:
          arg->v_int16 = ((guint32*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT16:
          arg->v_uint16 = ((guint32*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT32:
          arg->v_int32 = ((guint32*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT32:
          arg->v_uint32 = ((guint32*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT64:
          arg->v_int64 = ((guint32*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT64:
          arg->v_uint64 = ((guint32*)data)[j];	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          arg->v_float = ((guint32*)data)[j];	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          arg->v_double = ((guint32*)data)[j];	return TRUE;
/*
#if GLIB_SIZEOF_LONG == 4
        case GI_TYPE_TAG_GTYPE:
          ((GType*)arg)[i] = ((guint32*)data)[j];	return TRUE;
#endif
#if GLIB_SIZEOF_VOID_P == 4
        case GI_TYPE_TAG_INTERFACE:
          ((gpointer*)arg)[i] = ((guint32*)data)[j];	return TRUE;
#endif
*/
        default:
          return FALSE;
      }

    case mxUINT64_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          arg->v_boolean = ((guint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT8:
          arg->v_int8 = ((guint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT8:
          arg->v_uint8 = ((guint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT16:
          arg->v_int16 = ((guint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT16:
          arg->v_uint16 = ((guint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT32:
          arg->v_int32 = ((guint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT32:
          arg->v_uint32 = ((guint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INT64:
          arg->v_int64 = ((guint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_UINT64:
          arg->v_uint64 = ((guint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          arg->v_float = ((guint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          arg->v_double = ((guint64*)data)[j];	return TRUE;
/*
        case GI_TYPE_TAG_GTYPE:
          ((GType*)arg)[i] = ((guint64*)data)[j];	return TRUE;
        case GI_TYPE_TAG_INTERFACE:
          ((gpointer*)arg)[i] = ((guint64*)data)[j];	return TRUE;
*/
        default:
          return FALSE;
      }

    case mxUNKNOWN_CLASS:
    case mxCELL_CLASS:
    case mxSTRUCT_CLASS:
    case mxVOID_CLASS:
    case mxFUNCTION_CLASS:
    default:
      return FALSE;
  }

  return FALSE;
}

gboolean arg_set_mxArray(GArgument *arg, GITypeTag type, 
                         gpointer data, mxClassID mxType, guint j)
{
  g_return_val_if_fail(arg && data, FALSE);
  g_return_val_if_fail(type < GI_TYPE_TAG_GTYPE, FALSE);
  g_return_val_if_fail(type != GI_TYPE_TAG_VOID, FALSE);
  switch (mxType)
  {
    case mxLOGICAL_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          ((mxLogical*)data)[j] = arg->v_boolean;	return TRUE;
        case GI_TYPE_TAG_INT8:
          ((mxLogical*)data)[j] = arg->v_int8;	return TRUE;
        case GI_TYPE_TAG_UINT8:
          ((mxLogical*)data)[j] = arg->v_uint8;	return TRUE;
        case GI_TYPE_TAG_INT16:
          ((mxLogical*)data)[j] = arg->v_int16;	return TRUE;
        case GI_TYPE_TAG_UINT16:
          ((mxLogical*)data)[j] = arg->v_uint16;	return TRUE;
        case GI_TYPE_TAG_INT32:
          ((mxLogical*)data)[j] = arg->v_int32;	return TRUE;
        case GI_TYPE_TAG_UINT32:
          ((mxLogical*)data)[j] = arg->v_uint32;	return TRUE;
        case GI_TYPE_TAG_INT64:
          ((mxLogical*)data)[j] = arg->v_int64;	return TRUE;
        case GI_TYPE_TAG_UINT64:
          ((mxLogical*)data)[j] = arg->v_uint64;	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          ((mxLogical*)data)[j] = arg->v_float;	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          ((mxLogical*)data)[j] = arg->v_double;	return TRUE;
        default:
          return FALSE;
      }
      
    case mxCHAR_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          ((mxChar*)data)[j] = arg->v_boolean;	return TRUE;
        case GI_TYPE_TAG_INT8:
          ((mxChar*)data)[j] = arg->v_int8;	return TRUE;
        case GI_TYPE_TAG_UINT8:
          ((mxChar*)data)[j] = arg->v_uint8;	return TRUE;
        case GI_TYPE_TAG_INT16:
          ((mxChar*)data)[j] = arg->v_int16;	return TRUE;
        case GI_TYPE_TAG_UINT16:
          ((mxChar*)data)[j] = arg->v_uint16;	return TRUE;
        case GI_TYPE_TAG_INT32:
          ((mxChar*)data)[j] = arg->v_int32;	return TRUE;
        case GI_TYPE_TAG_UINT32:
          ((mxChar*)data)[j] = arg->v_uint32;	return TRUE;
        case GI_TYPE_TAG_INT64:
          ((mxChar*)data)[j] = arg->v_int64;	return TRUE;
        case GI_TYPE_TAG_UINT64:
          ((mxChar*)data)[j] = arg->v_uint64;	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          ((mxChar*)data)[j] = arg->v_float;	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          ((mxChar*)data)[j] = arg->v_double;	return TRUE;
        default:
          return FALSE;
      }

    case mxDOUBLE_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          ((gdouble*)data)[j] = arg->v_boolean;	return TRUE;
        case GI_TYPE_TAG_INT8:
          ((gdouble*)data)[j] = arg->v_int8;	return TRUE;
        case GI_TYPE_TAG_UINT8:
          ((gdouble*)data)[j] = arg->v_uint8;	return TRUE;
        case GI_TYPE_TAG_INT16:
          ((gdouble*)data)[j] = arg->v_int16;	return TRUE;
        case GI_TYPE_TAG_UINT16:
          ((gdouble*)data)[j] = arg->v_uint16;	return TRUE;
        case GI_TYPE_TAG_INT32:
          ((gdouble*)data)[j] = arg->v_int32;	return TRUE;
        case GI_TYPE_TAG_UINT32:
          ((gdouble*)data)[j] = arg->v_uint32;	return TRUE;
        case GI_TYPE_TAG_INT64:
          ((gdouble*)data)[j] = arg->v_int64;	return TRUE;
        case GI_TYPE_TAG_UINT64:
          ((gdouble*)data)[j] = arg->v_uint64;	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          ((gdouble*)data)[j] = arg->v_float;	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          ((gdouble*)data)[j] = arg->v_double;	return TRUE;
        default:
          return FALSE;
      }

    case mxSINGLE_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          ((gfloat*)data)[j] = arg->v_boolean;	return TRUE;
        case GI_TYPE_TAG_INT8:
          ((gfloat*)data)[j] = arg->v_int8;	return TRUE;
        case GI_TYPE_TAG_UINT8:
          ((gfloat*)data)[j] = arg->v_uint8;	return TRUE;
        case GI_TYPE_TAG_INT16:
          ((gfloat*)data)[j] = arg->v_int16;	return TRUE;
        case GI_TYPE_TAG_UINT16:
          ((gfloat*)data)[j] = arg->v_uint16;	return TRUE;
        case GI_TYPE_TAG_INT32:
          ((gfloat*)data)[j] = arg->v_int32;	return TRUE;
        case GI_TYPE_TAG_UINT32:
          ((gfloat*)data)[j] = arg->v_uint32;	return TRUE;
        case GI_TYPE_TAG_INT64:
          ((gfloat*)data)[j] = arg->v_int64;	return TRUE;
        case GI_TYPE_TAG_UINT64:
          ((gfloat*)data)[j] = arg->v_uint64;	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          ((gfloat*)data)[j] = arg->v_float;	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          ((gfloat*)data)[j] = arg->v_double;	return TRUE;
        default:
          return FALSE;
      }

    case mxINT8_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          ((gint8*)data)[j] = arg->v_boolean;	return TRUE;
        case GI_TYPE_TAG_INT8:
          ((gint8*)data)[j] = arg->v_int8;		return TRUE;
        case GI_TYPE_TAG_UINT8:
          ((gint8*)data)[j] = arg->v_uint8;	return TRUE;
        case GI_TYPE_TAG_INT16:
          ((gint8*)data)[j] = arg->v_int16;	return TRUE;
        case GI_TYPE_TAG_UINT16:
          ((gint8*)data)[j] = arg->v_uint16;	return TRUE;
        case GI_TYPE_TAG_INT32:
          ((gint8*)data)[j] = arg->v_int32;	return TRUE;
        case GI_TYPE_TAG_UINT32:
          ((gint8*)data)[j] = arg->v_uint32;	return TRUE;
        case GI_TYPE_TAG_INT64:
          ((gint8*)data)[j] = arg->v_int64;	return TRUE;
        case GI_TYPE_TAG_UINT64:
          ((gint8*)data)[j] = arg->v_uint64;	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          ((gint8*)data)[j] = arg->v_float;	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          ((gint8*)data)[j] = arg->v_double;	return TRUE;
        default:
          return FALSE;
      }

    case mxINT16_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          ((gint16*)data)[j] = arg->v_boolean;	return TRUE;
        case GI_TYPE_TAG_INT8:
          ((gint16*)data)[j] = arg->v_int8;	return TRUE;
        case GI_TYPE_TAG_UINT8:
          ((gint16*)data)[j] = arg->v_uint8;	return TRUE;
        case GI_TYPE_TAG_INT16:
          ((gint16*)data)[j] = arg->v_int16;	return TRUE;
        case GI_TYPE_TAG_UINT16:
          ((gint16*)data)[j] = arg->v_uint16;	return TRUE;
        case GI_TYPE_TAG_INT32:
          ((gint16*)data)[j] = arg->v_int32;	return TRUE;
        case GI_TYPE_TAG_UINT32:
          ((gint16*)data)[j] = arg->v_uint32;	return TRUE;
        case GI_TYPE_TAG_INT64:
          ((gint16*)data)[j] = arg->v_int64;	return TRUE;
        case GI_TYPE_TAG_UINT64:
          ((gint16*)data)[j] = arg->v_uint64;	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          ((gint16*)data)[j] = arg->v_float;	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          ((gint16*)data)[j] = arg->v_double;	return TRUE;
        default:
          return FALSE;
      }

    case mxINT32_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          ((gint32*)data)[j] = arg->v_boolean;	return TRUE;
        case GI_TYPE_TAG_INT8:
          ((gint32*)data)[j] = arg->v_int8;	return TRUE;
        case GI_TYPE_TAG_UINT8:
          ((gint32*)data)[j] = arg->v_uint8;	return TRUE;
        case GI_TYPE_TAG_INT16:
          ((gint32*)data)[j] = arg->v_int16;	return TRUE;
        case GI_TYPE_TAG_UINT16:
          ((gint32*)data)[j] = arg->v_uint16;	return TRUE;
        case GI_TYPE_TAG_INT32:
          ((gint32*)data)[j] = arg->v_int32;	return TRUE;
        case GI_TYPE_TAG_UINT32:
          ((gint32*)data)[j] = arg->v_uint32;	return TRUE;
        case GI_TYPE_TAG_INT64:
          ((gint32*)data)[j] = arg->v_int64;	return TRUE;
        case GI_TYPE_TAG_UINT64:
          ((gint32*)data)[j] = arg->v_uint64;	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          ((gint32*)data)[j] = arg->v_float;	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          ((gint32*)data)[j] = arg->v_double;	return TRUE;
/*
#if GLIB_SIZEOF_LONG == 4
        case GI_TYPE_TAG_GTYPE:
          ((gint32*)data)[j] = ((GType*)arg)[i];	return TRUE;
#endif
#if GLIB_SIZEOF_VOID_P == 4
        case GI_TYPE_TAG_INTERFACE:
          ((gint32*)data)[j] = ((gpointer*)arg)[i];	return TRUE;
#endif
*/
        default:
          return FALSE;
      }

    case mxINT64_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          ((gint64*)data)[j] = arg->v_boolean;	return TRUE;
        case GI_TYPE_TAG_INT8:
          ((gint64*)data)[j] = arg->v_int8;	return TRUE;
        case GI_TYPE_TAG_UINT8:
          ((gint64*)data)[j] = arg->v_uint8;	return TRUE;
        case GI_TYPE_TAG_INT16:
          ((gint64*)data)[j] = arg->v_int16;	return TRUE;
        case GI_TYPE_TAG_UINT16:
          ((gint64*)data)[j] = arg->v_uint16;	return TRUE;
        case GI_TYPE_TAG_INT32:
          ((gint64*)data)[j] = arg->v_int32;	return TRUE;
        case GI_TYPE_TAG_UINT32:
          ((gint64*)data)[j] = arg->v_uint32;	return TRUE;
        case GI_TYPE_TAG_INT64:
          ((gint64*)data)[j] = arg->v_int64;	return TRUE;
        case GI_TYPE_TAG_UINT64:
          ((gint64*)data)[j] = arg->v_uint64;	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          ((gint64*)data)[j] = arg->v_float;	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          ((gint64*)data)[j] = arg->v_double;	return TRUE;
/*
        case GI_TYPE_TAG_GTYPE:
          ((gint64*)data)[j] = ((GType*)arg)[i];	return TRUE;
        case GI_TYPE_TAG_INTERFACE:
          ((gint64*)data)[j] = ((gpointer*)arg)[i];	return TRUE;
 */
        default:
          return FALSE;
      }

    case mxUINT8_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          ((guint8*)data)[j] = arg->v_boolean;	return TRUE;
        case GI_TYPE_TAG_INT8:
          ((guint8*)data)[j] = arg->v_int8;	return TRUE;
        case GI_TYPE_TAG_UINT8:
          ((guint8*)data)[j] = arg->v_uint8;	return TRUE;
        case GI_TYPE_TAG_INT16:
          ((guint8*)data)[j] = arg->v_int16;	return TRUE;
        case GI_TYPE_TAG_UINT16:
          ((guint8*)data)[j] = arg->v_uint16;	return TRUE;
        case GI_TYPE_TAG_INT32:
          ((guint8*)data)[j] = arg->v_int32;	return TRUE;
        case GI_TYPE_TAG_UINT32:
          ((guint8*)data)[j] = arg->v_uint32;	return TRUE;
        case GI_TYPE_TAG_INT64:
          ((guint8*)data)[j] = arg->v_int64;	return TRUE;
        case GI_TYPE_TAG_UINT64:
          ((guint8*)data)[j] = arg->v_uint64;	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          ((guint8*)data)[j] = arg->v_float;	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          ((guint8*)data)[j] = arg->v_double;	return TRUE;
        default:
          return FALSE;
      }

    case mxUINT16_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          ((guint16*)data)[j] = arg->v_boolean;	return TRUE;
        case GI_TYPE_TAG_INT8:
          ((guint16*)data)[j] = arg->v_int8;	return TRUE;
        case GI_TYPE_TAG_UINT8:
          ((guint16*)data)[j] = arg->v_uint8;	return TRUE;
        case GI_TYPE_TAG_INT16:
          ((guint16*)data)[j] = arg->v_int16;	return TRUE;
        case GI_TYPE_TAG_UINT16:
          ((guint16*)data)[j] = arg->v_uint16;	return TRUE;
        case GI_TYPE_TAG_INT32:
          ((guint16*)data)[j] = arg->v_int32;	return TRUE;
        case GI_TYPE_TAG_UINT32:
          ((guint16*)data)[j] = arg->v_uint32;	return TRUE;
        case GI_TYPE_TAG_INT64:
          ((guint16*)data)[j] = arg->v_int64;	return TRUE;
        case GI_TYPE_TAG_UINT64:
          ((guint16*)data)[j] = arg->v_uint64;	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          ((guint16*)data)[j] = arg->v_float;	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          ((guint16*)data)[j] = arg->v_double;	return TRUE;
        default:
          return FALSE;
      }

    case mxUINT32_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          ((guint32*)data)[j] = arg->v_boolean;	return TRUE;
        case GI_TYPE_TAG_INT8:
          ((guint32*)data)[j] = arg->v_int8;	return TRUE;
        case GI_TYPE_TAG_UINT8:
          ((guint32*)data)[j] = arg->v_uint8;	return TRUE;
        case GI_TYPE_TAG_INT16:
          ((guint32*)data)[j] = arg->v_int16;	return TRUE;
        case GI_TYPE_TAG_UINT16:
          ((guint32*)data)[j] = arg->v_uint16;	return TRUE;
        case GI_TYPE_TAG_INT32:
          ((guint32*)data)[j] = arg->v_int32;	return TRUE;
        case GI_TYPE_TAG_UINT32:
          ((guint32*)data)[j] = arg->v_uint32;	return TRUE;
        case GI_TYPE_TAG_INT64:
          ((guint32*)data)[j] = arg->v_int64;	return TRUE;
        case GI_TYPE_TAG_UINT64:
          ((guint32*)data)[j] = arg->v_uint64;	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          ((guint32*)data)[j] = arg->v_float;	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          ((guint32*)data)[j] = arg->v_double;	return TRUE;
/*
#if GLIB_SIZEOF_LONG == 4
        case GI_TYPE_TAG_GTYPE:
          ((guint32*)data)[j] = ((GType*)arg)[i];	return TRUE;
#endif
#if GLIB_SIZEOF_VOID_P == 4
        case GI_TYPE_TAG_INTERFACE:
          ((guint32*)data)[j] = ((gpointer*)arg)[i];	return TRUE;
#endif
*/
        default:
          return FALSE;
      }

    case mxUINT64_CLASS:
      switch (type)
      {
        case GI_TYPE_TAG_BOOLEAN:
          ((guint64*)data)[j] = arg->v_boolean;	return TRUE;
        case GI_TYPE_TAG_INT8:
          ((guint64*)data)[j] = arg->v_int8;	return TRUE;
        case GI_TYPE_TAG_UINT8:
          ((guint64*)data)[j] = arg->v_uint8;	return TRUE;
        case GI_TYPE_TAG_INT16:
          ((guint64*)data)[j] = arg->v_int16;	return TRUE;
        case GI_TYPE_TAG_UINT16:
          ((guint64*)data)[j] = arg->v_uint16;	return TRUE;
        case GI_TYPE_TAG_INT32:
          ((guint64*)data)[j] = arg->v_int32;	return TRUE;
        case GI_TYPE_TAG_UINT32:
          ((guint64*)data)[j] = arg->v_uint32;	return TRUE;
        case GI_TYPE_TAG_INT64:
          ((guint64*)data)[j] = arg->v_int64;	return TRUE;
        case GI_TYPE_TAG_UINT64:
          ((guint64*)data)[j] = arg->v_uint64;	return TRUE;
        case GI_TYPE_TAG_FLOAT:
          ((guint64*)data)[j] = arg->v_float;	return TRUE;
        case GI_TYPE_TAG_DOUBLE:
          ((guint64*)data)[j] = arg->v_double;	return TRUE;
/*
        case GI_TYPE_TAG_GTYPE:
          ((guint64*)data)[j] = ((GType*)arg)[i];	return TRUE;
        case GI_TYPE_TAG_INTERFACE:
          ((guint64*)data)[j] = ((gpointer*)arg)[i];	return TRUE;
 */
        default:
          return FALSE;
      }

    case mxUNKNOWN_CLASS:
    case mxCELL_CLASS:
    case mxSTRUCT_CLASS:
    case mxVOID_CLASS:
    case mxFUNCTION_CLASS:
    default:
      return FALSE;
  }

  return FALSE;
}

